﻿/*
 * Authors:
 *   钟峰(Popeye Zhong) <9555843@qq.com>
 *
 * Copyright (c) 2017 Flagwind Game Studio. All rights reserved.
 */

using System;
using System.Security.Cryptography;

namespace Flagwind.Gaming
{
	/// <summary>
	/// 表示洗牌器的静态类。
	/// </summary>
	public static class Shuffler
	{
		#region 私有变量
		private static readonly RandomNumberGenerator _random = RandomNumberGenerator.Create();
		#endregion

		#region 公共方法
		/// <summary>
		/// 洗牌方法，该方法采用“Fisher Yates”算法实现。
		/// </summary>
		/// <param name="cards">指定的卡牌数组。</param>
		public static void Shuffle(byte[] cards)
		{
			if(cards == null || cards.Length == 0)
				throw new ArgumentNullException(nameof(cards));

			for(int i = cards.Length - 1; i >= 0; i--)
			{
				var index = GetRandomNumber() % (i + 1);

				if(index == i)
					continue;

				var temp = cards[index];
				cards[index] = cards[i];
				cards[i] = temp;
			}
		}

		/// <summary>
		/// 洗牌方法，该方法采用“Fisher Yates”算法实现。
		/// </summary>
		/// <param name="count">指定一副卡牌的总数。譬如含大小王的扑克牌为54，不含大小王则为52；标准麻将为136。</param>
		/// <returns>返回指定数量的卡牌序号数组，数组元素值为卡牌集中的序号。</returns>
		/// <remarks>
		///		<para>标准麻将的牌子包括：</para>
		///		<list type="bullet">
		///			<item>风牌：东、南、西、北；各4张，共16张。</item>
		///			<item>箭牌：中、发、白；各4张，共12张。</item>
		///			<item>万子牌：一万至九万；各4张，共36张。</item>
		///			<item>筒子牌（饼子）：一筒至九筒；各4张，共36张。</item>
		///			<item>索子牌（条子）：一索至九索；各4张，共36张。</item>
		///		</list>
		/// </remarks>
		public static byte[] Shuffle(int count)
		{
			if(count < 1 || count > byte.MaxValue)
				throw new ArgumentOutOfRangeException(nameof(count));

			var cards = new byte[count];

			for(int i = cards.Length - 1; i >= 0; i--)
			{
				var index = GetRandomNumber() % (i + 1);

				if(cards[i] == 0 && i > 0)
					cards[i] = (byte)i;

				if(cards[index] == 0 && index > 0)
					cards[index] = (byte)index;

				if(index == i)
					continue;

				var temp = cards[index];
				cards[index] = cards[i];
				cards[i] = temp;
			}

			return cards;
		}
		#endregion

		#region 私有方法
		private static int GetRandomNumber()
		{
			var bytes = new byte[4];
			_random.GetBytes(bytes);
			return Math.Abs(BitConverter.ToInt32(bytes, 0));
		}
		#endregion
	}
}
